#include "Model.h"
#include <sstream>
#include "Library.h"

const HashedString Model::type("Model");

Mesh::Vector_t& Model::getMeshes()
{
	return m_meshes;
}

//BindingNode::Map_t& Model::getNodes()
//{
//	return m_nodes;
//}
//
//HashedString::Vector_t& Model::getParticleSystems()
//{
//	return m_particleSystems;
//}
//
HashedString& Model::getSkeletonName()
{
	return m_skeletonName;
}


const HashedString& Model::Type()
{
	return type;
}

const HashedString& Model::getResourceType() const
{
	return type;
}

bool Model::ToXML( tinyxml2::XMLPrinter& printer )
{
	char buffer[50];
	printer.OpenElement("Meshes");
	printer.PushAttribute("Count", m_meshes.size());

	for (Mesh::Vector_t::iterator it = m_meshes.begin(); it != m_meshes.end(); ++it)
	{
		printer.OpenElement("Mesh");
#pragma region Material
		printer.OpenElement("Material");
		
		Material& material((*it)->getMaterial());

		sprintf_s(buffer, 50, "%.3f %.3f %.3f %.3f", material.m_ambient[0], material.m_ambient[1], material.m_ambient[2], material.m_ambient[3]);
		printer.PushAttribute("Ambient", buffer);
		sprintf_s(buffer, 50, "%.3f %.3f %.3f %.3f", material.m_diffuse[0], material.m_diffuse[1], material.m_diffuse[2], material.m_diffuse[3]);
		printer.PushAttribute("Diffuse", buffer);
		sprintf_s(buffer, 50, "%.3f %.3f %.3f %.3f", material.m_emmisive[0], material.m_emmisive[1], material.m_emmisive[2], material.m_emmisive[3]);
		printer.PushAttribute("Emmisive", buffer);
		sprintf_s(buffer, 50, "%.3f %.3f %.3f %.3f", material.m_specular[0], material.m_specular[1], material.m_specular[2], material.m_specular[3]);
		printer.PushAttribute("Specular", buffer);
		printer.PushAttribute("Shininess", material.m_shininess);
		printer.PushAttribute("Shader", material.m_shader.getString().c_str());

		for (HashedString::Vector_t::iterator tex = material.m_textures.begin(); tex != material.m_textures.end(); ++tex)
		{
			printer.OpenElement("Texture");
			printer.PushAttribute("Name", (*tex).getString().c_str());
			printer.CloseElement();
		}
		printer.CloseElement();
#pragma endregion
#pragma region VertexContainer
		printer.OpenElement("VertexContainer");
		Pointer<IVertexContainer> vertex((*it)->getVertexContainer());

		std::vector<unsigned short>& indices(vertex->getIndices());
		const VertexDeclaration& declaration(vertex->getVertexDeclaration());
		unsigned vertexCount(vertex->getVerticeCount());
		unsigned char* vertexPtr((unsigned char*)(vertex->getVertexPointer()));
		bool isIBO(vertex->IsIBO());
		bool isVBO(vertex->IsVBO());
			
		printer.PushAttribute("VBO", isVBO);
		printer.PushAttribute("IBO", isIBO);
#pragma region Declaration
			printer.OpenElement("Declaration");
			printer.PushAttribute("Size", declaration.m_vertexSize);

			for (VertexType::Vector_t::const_iterator param = declaration.m_parameters.begin(); param != declaration.m_parameters.end(); ++param)
			{
				printer.OpenElement("Parameter");
				printer.PushAttribute("Offset", param->m_offset);
				printer.PushAttribute("Size", param->m_size);
				printer.PushAttribute("Parameter", param->m_parameter);
				printer.CloseElement();
			}

			printer.CloseElement();
#pragma endregion
#pragma region Vertices
			printer.OpenElement("Vertices");
			printer.PushAttribute("Count", vertexCount);

			unsigned char* ptr = vertexPtr;

			for (unsigned int i = 0; i <vertexCount*declaration.m_vertexSize; ++i)
			{
				printer.PushText(*(unsigned char*)(ptr));
				printer.PushText(" ");
				++ptr;
			}

			printer.CloseElement();
#pragma endregion
#pragma region Indices
			printer.OpenElement("Indices");
			printer.PushAttribute("Count", vertex->getIndiceCount());

			for (std::vector<unsigned short>::iterator index = indices.begin(); index != indices.end(); ++index)
			{
				printer.PushText(*index);
				printer.PushText(" ");
			}

			printer.CloseElement();
#pragma endregion
		printer.CloseElement();
#pragma endregion
		printer.CloseElement();
	}

	printer.CloseElement();
	
	return true;
}

bool Model::FromXML( tinyxml2::XMLElement* element )
{		
	HashedString name(element->Attribute("Name"));

		tinyxml2::XMLElement* meshes = element->FirstChildElement("Meshes");
		const unsigned int count = meshes->UnsignedAttribute("Count");
		tinyxml2::XMLElement* meshxml = meshes->FirstChildElement("Mesh");
		unsigned int counter = 0;

		Model* model = this;
		Pointer<Mesh> mesh;
	

		while (counter < count)
		{
			mesh = new Mesh();
	#pragma region Material
			Material& mat = mesh->getMaterial();
			tinyxml2::XMLElement* matXML = meshxml->FirstChildElement("Material");
			// uniforms
			{
				std::stringstream attrib(std::string(matXML->Attribute("Ambient")));
				attrib >> mat.m_ambient[0] >> mat.m_ambient[1] >> mat.m_ambient[2] >> mat.m_ambient[3];
			}
			{
				std::stringstream attrib(std::string(matXML->Attribute("Diffuse")));
				attrib >> mat.m_diffuse[0] >> mat.m_diffuse[1] >> mat.m_diffuse[2] >> mat.m_diffuse[3];
			}
			{
				std::stringstream attrib(std::string(matXML->Attribute("Emmisive")));
				attrib >> mat.m_emmisive[0] >> mat.m_emmisive[1] >> mat.m_emmisive[2] >> mat.m_emmisive[3];
			}
			{
				std::stringstream attrib(std::string(matXML->Attribute("Specular")));
				attrib >> mat.m_specular[0] >> mat.m_specular[1] >> mat.m_specular[2] >> mat.m_specular[3];
			}
			mat.m_shininess = matXML->FloatAttribute("Shininess");
			mat.m_shader = HashedString(matXML->Attribute("Shader"));
		
			for (tinyxml2::XMLElement* texture = matXML->FirstChildElement("Texture"); texture != NULL; texture = texture->NextSiblingElement("Texture"))
			{
				mat.m_textures.push_back(HashedString(texture->Attribute("Name")));
			}

	#pragma endregion

	#pragma region VerticeContainer
			Pointer<IVertexContainer>& vertices = mesh->getVertexContainer();
			tinyxml2::XMLElement* vertexContainer = meshxml->FirstChildElement("VertexContainer");
		
			bool IsVBO = vertexContainer->BoolAttribute("VBO");
			bool IsIBO = vertexContainer->BoolAttribute("IBO");

			tinyxml2::XMLElement* declaration = vertexContainer->FirstChildElement("Declaration");
			unsigned int size = declaration->UnsignedAttribute("Size");

			VertexType::Vector_t params;
			for (tinyxml2::XMLElement* parameter = declaration->FirstChildElement("Parameter"); parameter != NULL; parameter = parameter->NextSiblingElement())
				params.push_back(VertexType(parameter->UnsignedAttribute("Offset"), parameter->UnsignedAttribute("Size"),(VertexParameter)(parameter->UnsignedAttribute("Parameter"))));
		
			tinyxml2::XMLElement* verticeXML = vertexContainer->FirstChildElement("Vertices");
			vertices = new IVertexContainer(VertexDeclaration(params));

			vertices->resize(verticeXML->UnsignedAttribute("Count"));
			unsigned char* ptr = (unsigned char*)(vertices->getVertexPointer());

			{
				std::stringstream ss(std::string(verticeXML->FirstChild()->ToText()->Value()), std::stringstream::in | std::stringstream::out);
				for (unsigned int i = 0; i < vertices->getVerticeCount() * size; ++i)
				{
					unsigned int value;
					ss >> value;
					*ptr = value;;
					++ptr;
				}
			}

	#pragma endregion

	#pragma region Indices
			tinyxml2::XMLElement* indicesXML = vertexContainer->FirstChildElement("Indices");
			vertices->getIndices().resize(indicesXML->UnsignedAttribute("Count"));
			if (vertices->getIndices().size() > 0)
			{
				std::stringstream ss(std::string(indicesXML->FirstChild()->ToText()->Value()), std::stringstream::in | std::stringstream::out);
				for (unsigned int i = 0; i < vertices->getIndiceCount(); ++i)
				{
					unsigned short value = 0;
					ss >> value;
					((unsigned short*)vertices->getIndexPointer())[i] = value;
				}
			}
	#pragma endregion

			meshxml = meshxml->NextSiblingElement();
			model->getMeshes().push_back(mesh);
			++counter;
		}
		return true;
}